package graphic;
import events.ResizeEvent;
import flash.display.Loader;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.Lib;
import flash.net.URLLoader;
import flash.net.URLRequest;
import haxe.Timer;
import haxe.xml.Fast;
import phx.col.AABB;
import phx.col.SortedList;
import phx.World;

class SceneEvent
{
	public static var INITIALISATION_COMPLETE = "Scene_initialisation_complete";
}

/**
 * @author Damien Mabin
 */
class Scene extends Sprite
{
	
	private var m_NeededRenderObjects	: Array<{ modelName : String, file : String }>;
	private var m_WaitingInstanciation	: Array<Fast>;
	private var m_Name					: String;
	public  var m_CurrentRenderObjects	: Array<RenderObject>;
	private var m_PhysicalWorld			: World;
	public  var m_SceneContent			: Sprite;
	public	var	m_PlayPhysic			: Bool;
	private var m_AskedSize				: Point;
	private var m_ViewPoint				: Point;
	private var m_Zoom					: Float;
	
	public function new(?_Path:String) 
	{
		super();
		
		m_NeededRenderObjects = new Array<{ modelName : String, file : String }>();
		m_WaitingInstanciation = new Array<Fast>();
		m_CurrentRenderObjects = new Array<RenderObject>();
		m_Name = "";
		m_PlayPhysic = true;
		m_SceneContent = new Sprite();
		//m_DebugDraw = new FlashDraw(m_SceneContent.graphics);
		//m_DebugDraw.drawCircleRotation = true;
		m_AskedSize = new Point(100, 100);
		m_ViewPoint = new Point(0, 0);
		m_Zoom = 1.0;
		
		var worldSize : AABB = new AABB(-1000,-1000,1000,1000);
		var broadPhase : SortedList = new SortedList();
		m_PhysicalWorld = new phx.World(worldSize, broadPhase);
		m_PhysicalWorld.gravity = new phx.Vector(0, 0.5);
		
		if (_Path != null)
		{
			var sceneDescriptor : URLLoader = new URLLoader();
			sceneDescriptor.addEventListener(Event.COMPLETE, OnSceneDescriptorLoadingComplete);
			sceneDescriptor.load(new URLRequest(_Path));
		}
		this.addChild(m_SceneContent);
		
		this.addEventListener(ResizeEvent.EVENT, OnResize);
	}
	
	public inline function GetWorld():World
	{
		return m_PhysicalWorld;
	}
	
	public function Update(_Delta:Int):Void
	{
		
		if (m_PhysicalWorld != null && m_PlayPhysic)
		{
			m_PhysicalWorld.step(1, 20);
		}
		for (ro in m_CurrentRenderObjects)
		{

			ro.Update(_Delta);
		}
		this.graphics.clear();
		this.graphics.beginFill(0xBFCC85);
		this.graphics.drawRect(0, 0, width, height);
		this.graphics.endFill();
		
		var matrix : Matrix = new Matrix();
		matrix.translate(m_ViewPoint.x, m_ViewPoint.y);
		matrix.scale(m_Zoom, m_Zoom);
		
		m_SceneContent.transform.matrix = matrix;
	}
	
	public function GetRenderObjects():Array<RenderObject>
	{
		return m_CurrentRenderObjects;
	}
	
	/**
	 * This function will instanciate all the element of the scene.
	 * All the render object MUST have been loaded by the RessourceManager BEFORE calling the function.
	 */
	public function Instanciate():Void
	{
		while (m_WaitingInstanciation.length > 0)
		{
			var ro : RenderObject = RenderObject.LoadXML(m_WaitingInstanciation.pop());
			m_CurrentRenderObjects.push(ro);
			this.m_SceneContent.addChild(ro);
			if (ro.GetPhysicalBody() != null)
			{
				m_PhysicalWorld.addBody(ro.GetPhysicalBody());
			}
		}
	}
	
	public function GetNeededRenderObject():Array<{ modelName : String, file : String }>
	{
		return m_NeededRenderObjects;
	}
	
	public function AddRenderObject(_RO:RenderObject):Void
	{
		m_SceneContent.addChild(_RO);
		m_CurrentRenderObjects.push(_RO);
		if (_RO.GetPhysicalBody() != null && m_PhysicalWorld != null)
		{
			m_PhysicalWorld.addBody(_RO.GetPhysicalBody());
			m_PhysicalWorld.sync(_RO.GetPhysicalBody());
		}
	}
	
	public function RemoveRenderObject(_RO:RenderObject):Void
	{
		if (m_CurrentRenderObjects.remove(_RO))
		{
			m_SceneContent.removeChild(_RO);
			if (_RO.GetPhysicalBody() != null && m_PhysicalWorld != null)
			{
				m_PhysicalWorld.removeBody(_RO.GetPhysicalBody());
			}
		}
	}
	
	private function OnResize(_Evt:ResizeEvent):Void
	{
		m_AskedSize.x = _Evt.m_Width;
		m_AskedSize.y = _Evt.m_Height;
		
		this.graphics.clear();
		this.graphics.beginFill(0xBFCC85);
		this.graphics.drawRect(0, 0, _Evt.m_Width, _Evt.m_Height);
		this.graphics.endFill();
		
		this.scrollRect = new Rectangle(0, 0,_Evt.m_Width, _Evt.m_Height);
	}
	
	private function OnSceneDescriptorLoadingComplete(_Evt : Event) : Void
	{
		var xml : Fast = new Fast(Xml.parse(_Evt.target.data));
		LoadXML(xml);
		
		this.dispatchEvent(new Event(SceneEvent.INITIALISATION_COMPLETE));
	}
	
	public function SaveToXML():Xml
	{
		var res:Xml = Xml.createElement("Scene");
		res.set("name", m_Name);
		res.set("viewPointX", ""+m_ViewPoint.x);
		res.set("viewPointY", "" + m_ViewPoint.y);
		res.set("zoomLevel", ""+m_Zoom);
		
		for (ro in m_CurrentRenderObjects)
		{
			res.addChild(ro.SaveXML());
		}
		return res;
	}
	
	public function GetViewPoint():Point
	{
		return m_ViewPoint;
	}
	
	public function SetViewPoint(_Point:Point):Void
	{
		m_ViewPoint = _Point;
	}
	
	public function GetZoom():Float
	{
		return m_Zoom;
	}
	
	public function SetZoom(_Zoom:Float):Void
	{
		m_Zoom = _Zoom;
	}
	
	public function LoadXML(_Xml:Fast):Void
	{
		m_Name = _Xml.node.Scene.att.name;
		m_ViewPoint.x = Std.parseFloat(_Xml.node.Scene.att.viewPointX);
		m_ViewPoint.y = Std.parseFloat(_Xml.node.Scene.att.viewPointY);
		m_Zoom = Std.parseFloat(_Xml.node.Scene.att.zoomLevel);
		
		var worldSize : AABB = new AABB(-1000,-1000,1000,1000);
		var broadPhase : SortedList = new SortedList();
		m_PhysicalWorld = new phx.World(worldSize, broadPhase);
		m_PhysicalWorld.gravity = new phx.Vector(0, 0.5);
		
		for (ro in _Xml.node.Scene.nodes.RenderObject)
		{
			m_WaitingInstanciation.push(ro);
			
			var isModelAlreadyNeeded : Bool = false;
			for (model in m_NeededRenderObjects)
			{
				if (model.file == ro.att.filePath && model.modelName == ro.att.modelName)
				{
					isModelAlreadyNeeded = true;
					break;
				}
			}
			if (!isModelAlreadyNeeded)
			{
				m_NeededRenderObjects.push({ file : ro.att.filePath, modelName : ro.att.modelName });
			}
		}
	}
}